#ifndef __BASIC_HIERARCHICAL_STRUCTURE_H__
#define __BASIC_HIERARCHICAL_STRUCTURE_H__

#include "basic/platform.h"
namespace basic {

#define M_INVALID_OFFSET (1 << 31)

enum class M_API_FUNC HSNodeResult {
  RESULT_SUCCESS = 0,
  RESULT_SAME_NODE,
  RESULT_ALREADY_INSERTED,
  RESULT_INVALID_VALUE,
  RESULT_ERROR,
};

enum class M_API_FUNC HSNodeRelation {
  RELATION_NEXT_SIBLING = 0,
  RELATION_PRE_SIBLING,
  RELATION_FIRST_CHILD,
  RELATION_LAST_CHILD,
};

class HSForest;

class M_API_FUNC HSNode {
public:
  HSNode(const HSNode&) = delete;
  HSNode* operator=(const HSNode&) = delete;
  HSNode() {
  }
  virtual ~HSNode() {
    m_ref_pNodeParent = nullptr;
    m_ref_pNodePreSibling = nullptr;
    m_ref_pNodeNextSibling = nullptr;
    m_ref_pNodeFirstChild = nullptr;
  }
  virtual HSNode* Traverse() {
    return nullptr;
  }

  template<class T>
  T GetParent()const{
    return dynamic_cast<T>(m_ref_pNodeParent);
  }
  HSNode* GetParent()const{ return m_ref_pNodeParent; }

  template<class T>
  T GetFirstChild()const{
    return dynamic_cast<T>(m_ref_pNodeFirstChild);
  }
  HSNode* GetFirstChild()const{ return m_ref_pNodeFirstChild; }

  template<class T>
  T GetNextSibling()const{
    return dynamic_cast<T>(m_ref_pNodeNextSibling);
  }
  HSNode* GetNextSibling()const{ return m_ref_pNodeNextSibling; }

  template<class T>
  T GetPreSibling()const{
    return dynamic_cast<T>(m_ref_pNodePreSibling);
  }
  HSNode* GetPreSibling()const{ return m_ref_pNodePreSibling; }

private:
  friend class HSForest;
  friend class HSProcessor;
  HSNode* m_ref_pNodeParent{nullptr};
  HSNode* m_ref_pNodePreSibling{nullptr};
  HSNode* m_ref_pNodeNextSibling{nullptr};
  HSNode* m_ref_pNodeFirstChild{nullptr};
};

class M_API_FUNC HSForest {
public:
  HSForest();
  virtual ~HSForest();

  bool IsRootNode(const HSNode* node) const;

  bool IsLeafNode(const HSNode* node) const;

  bool IsNodeInForest(const HSNode* node) const;
  bool IsSibling(const HSNode* nodea, const HSNode* nodeb) const;

  bool IsDirectDependent(const HSNode* node, const HSNode* ancestorNode) const;

  HSNode* GetFirstChild(HSNode* node);
  HSNode* GetLastChild(HSNode* node);

  HSNode* GetSibling(HSNode* node, int offset, bool notNull = true);
  HSNode* GetNextSibling(HSNode* node);
  HSNode* GetPreSibling(HSNode* node);

  HSNode* GetParent(HSNode* node);

  HSNode* GetRoot(HSNode* node);

  int GetOffset(const HSNode* node) const;

  HSNodeResult Remove(HSNode* node);

  HSNodeResult Insert(HSNode* node, HSNode* desNode, HSNodeRelation relation);

  int GetChildrenCount(const HSNode* node) const;

  HSNode* Traverse(const HSNode* node, bool begin);

  int GetDepth(HSNode* node) const;

private:
  HSNode m_soil;
  HSNode* m_ref_pNode = nullptr;
};

class M_API_FUNC HSProcessor final {
public:
  static HSNode* GetRoot(HSNode* node);
  static const HSNode* GetRoot(const HSNode* node);
  static HSNode* GetLastChild(HSNode* node);
  static const HSNode* GetLastChild(const HSNode* node);

  /**
   * offset of 0 means the node itself
   *	parentNode
   *		|---nodeA    -2 ~ -inf
   *		|---nodeB    -1
   *		|---nodeC--> node
   *		|---nodeD     1
   *		|---nodeE     2
   *		|---nodeF     3 ~ +inf
   */
  static HSNode* GetSiblingAt(HSNode* node, int offset);

  static void Remove(HSNode* node);
  static void RemoveChildren(HSNode* node);

  static void InsertAsFirstChild(HSNode* node, HSNode* desNode);
  static void InsertAsLastChild(HSNode* node, HSNode* desNode);
  static void InsertAsPreSibling(HSNode* node, HSNode* desNode);
  static void InsertAsNextSibling(HSNode* node, HSNode* desNode);

  static int GetChildrenCount(const HSNode* node);
  static bool IsDirectDependent(const HSNode* node, const HSNode* ancestorNode);

  /**
   * traverse all subnode of rootNode
      /// usage
      HSNode* pNode, nNode, cNode;/// these are temp nodes for traversing;
      HSNode* rootNode=<some-root-node>;

      HSNode*
   currentNode=HSProcessoer::Traverse(rootNode,pNode,nNode,cNode,true);
      while(currentNode){
      #if 1
          // some logic with currentNode;
          currentNode=HSProcessoer::Traverse(rootNode,pNode,nNode,cNode,false);
   // then continue; #endif

      #if 1
          // if you want to remove currentNode from tree, just do it;
          // causion: do NOT remove other nodes from tree while traversing;
          HSProcessoer::Remove(currentNode);
          delete currentNode;// you may delete if you want;
          cNode=nullptr; //delete parent no children nodes will be trversaled.
   do NOT forget to set cNode to nullptr; nNode=nullptr;// if you want to skip
   sibling nodes as well;
          //pNode=nullptr;// do NOT do this;
          currentNode=HSProcessoer::Traverse(rootNode,pNode,nNode,cNode,false);
      #endif

      #if 1
          if(<some-condition>){
              // if you want to stop traversing ,just break;
              break;
          }
      #endif
      }
  */
  static HSNode* Traverse(HSNode* rootNode, HSNode* pNode, HSNode* nNode, HSNode* cNode, bool begin);
};
} // namespace basic

#endif
